package com.ipemon.test;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.callback.Callback;

import jline.console.ConsoleReader;

import com.ipemon.exception.ProtocolDisRespectException;
import com.ipemon.model.Dresseur;
import com.ipemon.model.Foret;
import com.ipemon.model.IPemon;
import com.ipemon.server.CallBack;
import com.ipemon.server.DresseurUDP;

public class InterfaceDresseur implements CallBack{

	private Dresseur player;
	private ConsoleReader reader = null;
	private DresseurUDP dresseurNet = null;
	
	private String player2 = "";
	
	private static final int DISPLAY_MENU = 0;
	private static final int DISPLAY_USERNAME_MENU = 1;
	private static final int DISPLAY_VIEW_IPEMONS = 2;
	private static final int DISPLAY_SELECT_DEFAULT_IPEMON = 3;
	private static final int DISPLAY_CONNECT_FORET = 4;
	private static final int DISPLAY_CONNECT_DRESSEUR = 5;
	private static final int DISPLAY_ACCEPT_COMBAT = 6;
	
	private Map<Integer, String> mainMenuText = new HashMap<>();
	
	private static final int ERROR_INTEGER_EXPECTED = 10;
	private static final int ERROR_UNDEFINED_INTERVAL = 11;
	private static final int ERROR_USERNAME_HAS_SPACE = 12;
	private static final int ERROR_INCORRECT_IPEMON_NUMBER = 13;
	private static final int ERROR_INCORRECT_ADDRESS = 14;
	private static final int ERROR_SELECT_Y_OR_N = 15;
	
	
	
	public InterfaceDresseur() throws Exception{
		reader = new ConsoleReader();
		player = new Dresseur("player");
		dresseurNet = new DresseurUDP(player, this);
		
		initMainMenuText();
	}
	
	private void initMainMenuText() {
		mainMenuText.put(DISPLAY_USERNAME_MENU, "Change Username");
		mainMenuText.put(DISPLAY_VIEW_IPEMONS, "View IPemons");
		mainMenuText.put(DISPLAY_SELECT_DEFAULT_IPEMON, "Select default IPemon");
		mainMenuText.put(DISPLAY_CONNECT_FORET, "Get ipemon from Foret");
		mainMenuText.put(DISPLAY_CONNECT_DRESSEUR, "Combat with other Player");
	}
	
	public void setDresseur(Dresseur dresseur) {
		this.player = dresseur;
	}
	
	private void println() {
		println("");
	}
	
	private void println(String output) {
		try {
			reader.println(output);
		} catch (IOException e) {
			System.err.println("Could not print to stdout");
		}
	}
	
	private void print(String output) {
		try {
			reader.print(output);
		} catch (IOException e) {
			System.err.println("Could not print to stdout");
		}
		
	}
	
	private String getInput() {
		String input = "";
		
		try {
			reader.flush();
			input = reader.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return input;
	}
	
	private void displayHeader() {
		try {
			reader.clearScreen();
			println("IPEMON 1.0");
			println("");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void displayMenu() {
		displayMenu(0);
	}
	
	private void displayMenu(int errorCode) {
		displayHeader();
		println("Welcome " + player.getIdentifiant());
		println("");
		
		if(player.getIPemonList().size() > 0){
			println("Default pokemon: " + player.getDefaultIPemon().getName() 
					+ " [" + player.getDefaultIPemon().getEspeceName() + "]");
		} else {
			println("You have no IPemons");
		}
		println();
		
		int i = 1;
		while (i <= mainMenuText.size())
			println(i + ". " + mainMenuText.get(i++));
		
		println();
		println("Press q for exit");
		println();
		

		displayError(errorCode);
		print("Choice: ");
	}
	
	private void displayUsernameMenu(int errorCode) {
		displayHeader();
		println("");
		println("Change Username");
		println("");
		displayError(errorCode);
		println("Current Username: " + player.getIdentifiant());
		println("");
		print("New Username: ");
	}
	
	private void displayIPemons() {
		displayHeader();
		println("");
		println("IPemon List");
		println("");
		
		int nbIPemon = player.getIPemonList().size();
		
		if(nbIPemon > 0) { 

			println("You have " + nbIPemon +" IPemons");
			println();
			
			for(int i=0; i<nbIPemon; i++) {
				println(i+1 + ". " + player.getIPemonList().get(i).getName() + 
								" (" +player.getIPemonList().get(i).getEspeceName() + ") " );
			}
			println();
			
		}
		
	}
	
	private void displayIPemonsToSelect(int errorCode) {
		displayIPemons();
		int nbIpemons = player.getIPemonList().size();
		
		println("Press q to return to main menu ");
		println();
		
		if(nbIpemons > 1 ) {
			println("Current default IPemon : " + player.getDefaultIPemon().getIdentity() );
			println();
			print("Select new default IPemon : ");
			
		} else {
			println("No IPemon to select as default");
		}
		
	}
	
	private void displayViewIPemon(int errorCode) {
		displayIPemons();
		int nbIpemons = player.getIPemonList().size();
		
		println("Press q to return to main menu");
		println();
		
		if(nbIpemons >= 1 ) {
			print("Select IPemon to view details : ");
		} 

	}
	
	private void displayConnectForet(int errorCode) {
		displayHeader();
		displayError(errorCode);
		print("Enter Foret address in format \"Address:Port\" :  ");
		
	}
	
	private void displayConnectUser(int errorCode) {
		displayHeader();
		displayError(errorCode);
		print("Enter Dresseur address in format \"Address:Port\" : ");
	}
	
	private void displayAcceptCombat(int errorCode) {
		displayHeader();
		displayError(errorCode);
		print("\"" + player2 + "\" wants to combat with you, do you accept? (y/n) : ");
	}
	
	private void displayError(int errorCode) {
		
		switch(errorCode) {
			case ERROR_INTEGER_EXPECTED:
				
				println();
				println("---------------------------");
				println("| Expecting an integer !! |");
				println("---------------------------");
				println();
				break;
				
			case ERROR_UNDEFINED_INTERVAL:
				
				println();
				println("------------------------------------------");
				println("| Expecting an integer between 1 and "+ mainMenuText.size() +" !! |");
				println("------------------------------------------");
				println();
				break;
				
			case ERROR_USERNAME_HAS_SPACE:
				
				println();
				println("---------------------------------------");
				println("| Username should not contain space !! |");
				println("---------------------------------------");
				println();
				break;
				
			case ERROR_INCORRECT_IPEMON_NUMBER:
				println();
				println("---------------------------------");
				println("| Number between 1 and " + (player.getIPemonList().size()-1) + "  expected |");
				println("---------------------------------");
				println();
				break;
				
			case ERROR_INCORRECT_ADDRESS:
				println();
				println("----------------------------------------------------------");
				println("| Invalid address mismatch, please enter in correct format! |");
				println("----------------------------------------------------------");
				println();
				break;
				
			case ERROR_SELECT_Y_OR_N:
				println();
				println("------------------------");
				println("| Select either y or n! |");
				println("---------------------- -");
				println();
				break;
				
		}
			
		return;
	}
	private void displayController(int screenNumber) {
		displayController(screenNumber, 0);
	}
	
	private void displayController(int screenNumber, int errorCode) {
		
		switch(screenNumber) {
			case DISPLAY_MENU:
				displayMenu(errorCode);
				inputController(DISPLAY_MENU);
				break;
			case DISPLAY_USERNAME_MENU:
				displayUsernameMenu(errorCode);
				inputController(DISPLAY_USERNAME_MENU);
				break;
			case DISPLAY_VIEW_IPEMONS:
				displayViewIPemon(errorCode);
				inputController(DISPLAY_VIEW_IPEMONS);
				break;
			case DISPLAY_SELECT_DEFAULT_IPEMON:
				displayIPemonsToSelect(errorCode);
				inputController(DISPLAY_SELECT_DEFAULT_IPEMON);
				break;
			case DISPLAY_CONNECT_FORET:
				displayConnectForet(0);
				inputController(DISPLAY_CONNECT_FORET);
				break;
			case DISPLAY_CONNECT_DRESSEUR:
				displayConnectUser(errorCode);
				inputController(DISPLAY_CONNECT_DRESSEUR);
				break;
			case DISPLAY_ACCEPT_COMBAT:
				displayAcceptCombat(errorCode);
				inputController(DISPLAY_ACCEPT_COMBAT);
				break;
			
			default:
				displayController(DISPLAY_MENU);
		}
	}


	private void inputController(int screenNumber) {
		String input;
		input = getInput();

		switch(screenNumber) {
		case DISPLAY_MENU:
				int choice = 0;
				
				if(input.contains("q")) {
					System.exit(0);
				}
					
				
				try {
					choice = Integer.valueOf(input);
				} catch (NumberFormatException exp) {
					displayController(DISPLAY_MENU, ERROR_INTEGER_EXPECTED);
					break;
				}
				
				if(choice == 0 || choice > mainMenuText.size()) {
					displayController(DISPLAY_MENU, ERROR_UNDEFINED_INTERVAL);
					break;
				}
				
				displayController(choice);
				break;
				
			case DISPLAY_USERNAME_MENU:
				if(input.contains(" ")){
					displayController(DISPLAY_USERNAME_MENU, ERROR_USERNAME_HAS_SPACE);
					break;
				}
				
				player.setIdentifiant(input);
				displayController(DISPLAY_MENU);
				break;
			
			case DISPLAY_VIEW_IPEMONS:
			case DISPLAY_SELECT_DEFAULT_IPEMON:
				if(input.contains("q")) 
					displayController(DISPLAY_MENU);
					
				if(player.getIPemonList().size() > 1) {
					
					try {
						choice = Integer.valueOf(input);
					} catch (NumberFormatException exp) {
						displayController(DISPLAY_VIEW_IPEMONS, ERROR_INCORRECT_IPEMON_NUMBER);
						break;
					}
					
					if(choice == 0 || choice > player.getIPemonList().size()) {
						displayController(DISPLAY_VIEW_IPEMONS, ERROR_INCORRECT_IPEMON_NUMBER);
						break;
					}
					
					player.setDefaultIPemon(choice);
					
					displayController(DISPLAY_MENU);
					
				} else {
					displayController(DISPLAY_MENU);
				}
				
				break;
				
			case DISPLAY_CONNECT_FORET:
				
				if(input.contains("q")) {
					displayController(DISPLAY_MENU);
					break;
				}
				
				String serverPort[] = input.split(":");
				
				if(serverPort.length != 2) 
					displayController(DISPLAY_CONNECT_FORET, ERROR_INCORRECT_ADDRESS);
				
				int portNumber = 0;
				try {
					portNumber = Integer.valueOf(serverPort[1]);
				} catch(NumberFormatException exp) {
					displayController(DISPLAY_CONNECT_FORET, ERROR_INCORRECT_ADDRESS);
				}
				
				IPemon newIpemon;
				
				try {
					newIpemon = dresseurNet.getIPemonFromForet(serverPort[0], portNumber);
					player.addIPemon(newIpemon);
					
					print("New Pokemon received from Foret. Press q for main menu: ");
					
					inputController(DISPLAY_CONNECT_FORET);
				
				} catch (ProtocolDisRespectException e) {
					print("New Pokemon NOT received from Foret due to Protocol Error. Press q for main menu: ");
					
				} catch (UnknownHostException e) {
					print("Unknown Hostname for forest. Enter valid \"address:port\" : ");
					inputController(DISPLAY_CONNECT_FORET);
				}
				break;
				
			case DISPLAY_CONNECT_DRESSEUR:
				if(input.contains("q")) {
					displayController(DISPLAY_MENU);
					break;
				}
				
				String playerPort[] = input.split(":");
				
				if(playerPort.length != 2) 
					displayController(DISPLAY_CONNECT_DRESSEUR, ERROR_INCORRECT_ADDRESS);
				
				int port = 0;
				try {
					port = Integer.valueOf(playerPort[1]);
					
					println("Connecting to another player");
					
					player2 = dresseurNet.connectToAnotherDresseur(playerPort[0], port);
					
					if(player2 != "") {
						println(player2 + " accepted the challenge. Press any key to start the combat");
						getInput();
						
						
					} else {
						println("Challenge was not accepted by other player. Press any key for main menu");
						getInput();
					}

				} catch(NumberFormatException exp) {
					displayController(DISPLAY_CONNECT_DRESSEUR, ERROR_INCORRECT_ADDRESS);
					break;
				} catch (UnknownHostException e) {
					println("Address not found");
					getInput();
					e.printStackTrace();
				} catch (ProtocolDisRespectException e) {
					println("Protocol was not respected by the other side");
					getInput();
					e.printStackTrace();
				}
				displayController(DISPLAY_MENU);
				
				break;
				
			case DISPLAY_ACCEPT_COMBAT:
				if(input.equalsIgnoreCase("y") ){
					
				} else if (input.equalsIgnoreCase("n")) {
					
				} else {
					displayController(DISPLAY_ACCEPT_COMBAT, ERROR_SELECT_Y_OR_N);
				}
				
		}
		
	}
	
	@Override
	public void callIfReceived(int type, Object data) {
		
		switch(type) {
			case CallBack.COMBAT:
				player2 = (String) data;
				
				displayController(DISPLAY_ACCEPT_COMBAT);
				
		}
		
	}

	
	public void testNet() throws Exception {
		IPemon newIpemon = dresseurNet.getIPemonFromForet("localhost", 9000);
		
		println(newIpemon.toString());
	}
	
	public static void main(String[] args) throws Exception {
		Dresseur dresseur = new Dresseur("player");
		
/*		dresseur.addIPemon(foret.generateIpemon());
		dresseur.addIPemon(foret.generateIpemon());
		dresseur.addIPemon(foret.generateIpemon());
*/		
		InterfaceDresseur console = new InterfaceDresseur();
		console.setDresseur(dresseur);
		
		console.displayController(DISPLAY_MENU);
		
	}

	
	
}

